
import Data.IFF
import Data.Version (showVersion)
import Paths_hexena (version)
import System.Environment (getArgs, withArgs)
import System.Exit
import System.IO (hFlush, stdout)
import Text.ParserCombinators.Parsec
import Text.Printf

import Hexena.Chunks

data HexenaMonCmd = Contents
                  | Monitor (Maybe Int)
                  | Quit
                  | Symbol Char
                    deriving Show

symbol :: Parser Char
symbol = oneOf "!#$%&'()*+,-./:;<=>?[\\]^_{|}~"

controlESC :: Parser String
controlESC = char '\x1b' >>= \x -> return [x]

number :: Parser Int
number = read `fmap` many1 digit

hexNumber :: Parser Int
hexNumber = char '$' >> fmap (read . ("0x" ++)) (many1 hexDigit)

someNumber :: Parser Int
someNumber = hexNumber <|> number

spaces1 :: Parser ()
spaces1 = many1 space >> return ()

cmdContents :: Parser HexenaMonCmd
cmdContents = string "$" >> return Contents

cmdM :: Parser HexenaMonCmd
cmdM = string "m" >> (try $ do { spaces1 >> someNumber >>= return . Monitor . Just }
       <|> return (Monitor Nothing))

cmdQuit :: Parser HexenaMonCmd
cmdQuit = (controlESC <|> string "quit") >> return Quit

cmdSymbol :: Parser HexenaMonCmd
cmdSymbol = symbol >>= return . Symbol

commands :: Parser HexenaMonCmd
commands = cmdContents <|>
           cmdM <|>
           cmdQuit <|>
           cmdSymbol

commandLine :: Parser HexenaMonCmd
commandLine = do cmd <- commands
                 eof
                 return cmd

executeCmd :: [T] -> HexenaMonCmd -> IO ()
executeCmd _ Quit = putStrLn "Exiting HexenaMonitor" >> exitSuccess
executeCmd _ (Symbol s) = putStrLn $ "You gave me a Symbol " ++ show s ++ ", but I am not clever enough to know what to do with it..."
executeCmd iff Contents = print iff
executeCmd _ c = putStrLn (show c ++ "\nOK")

readNparse :: Int -> [T] -> IO ()
readNparse count iff = do
  putStr $ printf "($%04x) > " count
  hFlush stdout
  x <- getLine
  case parse commandLine "hxmonitor" x of
    Left err -> do print err
                   putStrLn "ERROR"
    Right val -> executeCmd iff val
  readNparse (count + 1) iff

main :: IO ()
main = do
  let banner = "Hexena Monitor V" ++ showVersion version
  putStrLn banner
  putStrLn $ replicate (length banner) '#' ++ "\n"
  (iffmap, iff) <- getArgs >>= loadMalwIFF' . head
  readNparse 0 iff
